﻿using System;

namespace Effects.Effects.Tools
{
    /// <summary>
    ///  Delay line with constant length and feedback adding it's output to the input.
    /// </summary>
    /// <remarks>
    ///  Provides similar functionality as <see cref="ConstantDelayLine"/> ConstantDelayLine. It allows user to set feedback parameter which allows mixing output of the delay line back to it's input.
    /// </remarks>
    public class FeedbackConstantDelayLine : DelayLine
    {
        /// <summary>
        ///   Buffer for storing samples.
        /// </summary>
        protected double[] data;

        /// <summary>
        ///  Lenght of buffer in samples.
        /// </summary>
        protected int length;

        /// <summary>
        ///  Index to the data array
        /// </summary>
        protected int index;

        /// <summary>
        ///  Feedback coef.
        /// </summary>
        protected double feedback;

        /// <summary>
        ///  1 - feedback
        /// </summary>
        protected double feedback_1;


        /// <summary>
        ///  Gets or sets feddback parameter.
        /// </summary>
        public double Feedback
        {
            get { return feedback; }
            set
            {
                if (value < 0 || value > 1)
                    throw new ArgumentException();

                this.feedback = value;
                this.feedback_1 = 1 - value;
            }
        }

        /// <summary>
        ///  Creates FeedbackConstantDelayLine with given length
        /// </summary>
        /// <param name="length"> Length in samples </param>
        /// <param name="feedback"> Feedback coef from range 0-1 </param>
        public FeedbackConstantDelayLine(int length,double feedback)
        {
            if (feedback < 0 || feedback > 1)
                throw new ArgumentException("Feedback must be in range 0-1");

            this.feedback = feedback;
            this.feedback_1 = 1 - feedback;
            
            this.length = length;
            this.index = 0;
            this.data = new double[length];
        }

        /// <summary>
        ///  Creates FeedbackConstantDelayLine with length that coresponds to the wanted delay time.
        /// </summary>
        /// <param name="time"> Delay time of each sample in miliseconds </param>
        /// <param name="sampleRate"> Sample rate of incoming samples </param>
        /// <param name="feedback"> Feedback coef from range 0-1 </param>
        public FeedbackConstantDelayLine(double time, int sampleRate,double feedback)
        {
            if (feedback < 0 || feedback > 1)
                throw new ArgumentException("Feedback must be in range 0-1");

            this.feedback = feedback;
            this.feedback_1 = 1 - feedback;

            this.length = Convert.ToInt32(time * sampleRate / (double) 1000);
            this.index = 0;
            this.data = new double[length];
        }

        /// <summary>
        ///  Puts given sample into the delay line and returns delayed sample.
        /// </summary>
        /// <param name="sample"> Sample to put into the delay line. </param>
        /// <returns> Returns delayed sample </returns>
        public override double Step(double sample)
        {
            double ret = data[index];

            data[index] = feedback * data[index] + feedback_1 * sample;
            index--;
            if (index < 0)
                index += length;

            return data[index];
        }

        /// <summary>
        ///  Returns top of the delay line.
        /// </summary>
        public override double Top
        {
            get { return data[index]; }
        }
    }
}